import shapely
from shapely.geometry import MultiPolygon,Polygon
import json
class CqlFilter2Sql():
    def filter(self,filterdict:dict):

        result=self.getop(filterdict["op"],filterdict["args"])
        return result

    def getop(self,opname:str,args:list):
        if opname=="and":
            result=self.op_and(args)
        elif opname=="or":
            result = self.op_or(args)
        elif opname=="not":
            result = self.op_or(args)
        elif opname=="=":
            result=self.op_equal(args)
        elif opname == "!=":
            result = self.op_notequal(args)
        elif opname == "<":
            result = self.op_lessthan(args)
        elif opname == "<=":
            result = self.op_lessthanequal(args)
        elif opname == ">":
            result = self.op_greaterthan(args)
        elif opname == ">=":
            result = self.op_greaterthanequal(args)
        elif opname == "isNull":
            result = self.op_isnull(args)
        elif opname == "like":
            result = self.op_like(args)
        elif opname == "between":
            result = self.op_between(args)
        elif opname == "in":
            result = self.op_in(args)
        elif opname == "s_intersects":
            result = self.op_s_intersects(args)
        elif opname == "s_contains":
            result = self.op_s_contains(args)
        elif opname == "s_within":
            result = self.op_s_within(args)
        elif opname == "t_intersects":
            result = self.op_t_intersects(args)
        else:
            result=""

        return result
    def op_and(self,args:list) -> str:
        resultlist = []
        for f in args:
            resultlist.append("("+self.filter(f)+")")
        result=" and ".join(resultlist)
        return result
    def op_or(self,args:list) -> str:
        resultlist = []
        for f in args:
            resultlist.append("("+self.filter(f)+")")
        result=" or ".join(resultlist)
        return result
    def op_not(self,args:list) -> str:
        if args[0]["op"]=="in":
            result=self.op_in(args[0]["args"])
            result=" not in ".join(result.split(" in "))
        return result
    def op_equal(self,args:list) -> str:
        if "property" in args[0].keys():
            if type(args[1])==int:
                property_value=str(args[1])
            else:
                property_value ="'"+ str(args[1])+"'"
            result=str(args[0]["property"])+" = "+property_value
        else:
            result=""
        return result

    def op_notequal(self, args: list) -> str:
        if "property" in args[0].keys():
            if type(args[1])==int:
                property_value=str(args[1])
            else:
                property_value ="'"+ str(args[1])+"'"
            result=str(args[0]["property"])+" != "+property_value
        else:
            result=""
        return result
    def op_lessthan(self, args: list) -> str:
        if "property" in args[0].keys():
            if type(args[1])==int:
                property_value=str(args[1])
            else:
                property_value ="'"+ str(args[1])+"'"
            result=str(args[0]["property"])+" < "+property_value
        else:
            result=""
        return result
    def op_lessthanequal(self, args: list) -> str:
        if "property" in args[0].keys():
            if type(args[1])==int:
                property_value=str(args[1])
            else:
                property_value ="'"+ str(args[1])+"'"
            result=str(args[0]["property"])+" <= "+property_value
        else:
            result=""
        return result

    def op_greaterthan(self, args: list) -> str:
        if "property" in args[0].keys():
            if type(args[1])==int:
                property_value=str(args[1])
            else:
                property_value ="'"+ str(args[1])+"'"
            result=str(args[0]["property"])+" > "+property_value
        else:
            result=""
        return result

    def op_greaterthanequal(self, args: list) -> str:
        if "property" in args[0].keys():
            if type(args[1])==int:
                property_value=str(args[1])
            else:
                property_value ="'"+ str(args[1])+"'"
            result=str(args[0]["property"])+" >= "+property_value
        else:
            result=""
        return result
    def op_isnull(self, args: list) -> str:
        if "property" in args[0].keys():
            result=str(args[0]["property"])+" is null "
        else:
            result=""
        return result
    def op_isnotnull(self, args: list) -> str:
        if "property" in args[0].keys():
            result=str(args[0]["property"])+" is not null "
        else:
            result=""
        return result
    def op_like(self, args: list) -> str:
        if "property" in args[0].keys():
            if type(args[1])==int:
                property_value=str(args[1])
            else:
                property_value ="'"+ str(args[1])+"'"
            result=str(args[0]["property"])+" like "+property_value
        else:
            result=""
        return result
    def op_between(self, args: list) -> str:
        if "property" in args[0].keys():
            if type(args[1])==int:
                property_value1=str(args[1])
                property_value2=str(args[2])
            else:
                property_value1 ="'"+ str(args[1])+"'"
                property_value2="'"+ str(args[2])+"'"
            result=str(args[0]["property"])+" between "+property_value1+" and "+property_value2
        else:
            result=""
        return result

    def op_in(self, args: list) -> str:
        if "property" in args[0].keys():
            if len(args[1]) < 2:
                args[1].append("XXXXXXX")
            inlist=tuple(i  for i in args[1])
            result = str(args[0]["property"]) + " in " + str(inlist)
        else:
            result = ""
        return result
    def op_s_intersects(self,args: list) -> str:
        if "property" in args[0].keys():
            geowkt=shapely.to_wkt(shapely.from_geojson(json.dumps(args[1])))
            geo = shapely.from_wkt(geowkt)
            if type(geo) == MultiPolygon:
                geos = shapely.unary_union(geo).convex_hull
                geowkt = shapely.to_wkt(geos)
                #bounds = geo.bounds
                #geowkt = shapely.to_wkt(shapely.box(bounds[0], bounds[1], bounds[2], bounds[3]))
                # combined_polygon = geo.convex_hull
                #
                # geowkt = shapely.to_wkt(combined_polygon)
            elif type(geo) == Polygon:
                polygoncount=len(geo.exterior.xy[0])
                if polygoncount>50:
                    geos = shapely.unary_union(geo).convex_hull
                    geowkt = shapely.to_wkt(geos)
            result =  "ST_INTERSECTS({},ST_SIMPLIFYPRESERVETOPOLOGY(({}),0.01))".format(str(args[0]["property"]),"'"+geowkt+"'")
        else:
            result = ""
        return result
    def op_s_contains(self,args: list) -> str:
        if "property" in args[0].keys():
            geowkt = shapely.to_wkt(shapely.from_geojson(json.dumps(args[1])))
            geo = shapely.from_wkt(geowkt)
            if type(geo) == MultiPolygon:
                geos = shapely.unary_union(geo).convex_hull
                geowkt = shapely.to_wkt(geos)
                #bounds = geo.bounds
                #geowkt = shapely.to_wkt(shapely.box(bounds[0], bounds[1], bounds[2], bounds[3]))
                # combined_polygon = geo.convex_hull
                #
                # geowkt = shapely.to_wkt(combined_polygon)
            elif type(geo) == Polygon:
                polygoncount=len(geo.exterior.xy[0])
                if polygoncount>50:
                    geos = shapely.unary_union(geo).convex_hull
                    geowkt = shapely.to_wkt(geos)
            result =  "ST_CONTAINS({},ST_SIMPLIFYPRESERVETOPOLOGY(({}),0.01))".format(str(args[0]["property"]),"'"+geowkt+"'")
        else:
            result = ""
        return result
    def op_s_within(self,args: list) -> str:
        if "property" in args[0].keys():
            geowkt = shapely.to_wkt(shapely.from_geojson(json.dumps(args[1])))
            geo = shapely.from_wkt(geowkt)
            if type(geo) == MultiPolygon:
                geos = shapely.unary_union(geo).convex_hull
                geowkt = shapely.to_wkt(geos)
                #bounds = geo.bounds
                #geowkt = shapely.to_wkt(shapely.box(bounds[0], bounds[1], bounds[2], bounds[3]))
                # combined_polygon = geo.convex_hull
                #
                # geowkt = shapely.to_wkt(combined_polygon)
            elif type(geo) == Polygon:
                polygoncount=len(geo.exterior.xy[0])
                if polygoncount>50:
                    geos = shapely.unary_union(geo).convex_hull
                    geowkt = shapely.to_wkt(geos)
            result =  "ST_WITHIN({},ST_SIMPLIFYPRESERVETOPOLOGY(({}),0.01))".format(str(args[0]["property"]),"'"+geowkt+"'")
        else:
            result = ""
        return result

    def op_t_intersects(self,args: list) -> str:
        if "property" in args[0].keys():
            if args[1]["interval"][0]=="..":
                result = str(args[0]["property"]) + " <= "+"'"+str(args[1]["interval"][1])+"'"
            elif args[1]["interval"][1]=="..":
                result = str(args[0]["property"]) + " >= " + "'"+str(args[1]["interval"][0])+"'"
            else:
                result = str(args[0]["property"]) + " >= " +"'"+ str(args[1]["interval"][0])+"'"+" and "+str(args[0]["property"]) + " <= " +"'"+ str(args[1]["interval"][1])+"'"
        else:
            result = ""
        return result